/* *
 * @program: VideoDirector
 *
 * @description: ${description}
 *
 * @author: 909845
 *
 * @create: 2018-12-22 09:45
***/

#include "MyTimer.h"



#include "MyTimer.h"
#include "PublicTools.h"

CMyTimer::CMyTimer(const shared_ptr<CEventEngine>& spThread) : m_spThread(spThread)
{
    if (nullptr == m_spThread)
    {
        shared_ptr<CEventThread> spThread = shared_ptr<CEventThread>(new CEventThread());
        spThread->Initialize();
        m_spThread = spThread;
    }
}

CMyTimer::~CMyTimer()
{
    Assert(m_mpOnceTask.empty());
    Assert(m_mpFixRateTask.empty());
    Assert(nullptr == m_spTimer);
    m_MsgMap = nullptr;
    m_spThread = nullptr;
}


Bool CMyTimer::Initial(weak_ptr<CTimerCallbackInstance> wpInstance)
{
    m_wpInstance = wpInstance;
    return Initial();
}

Bool CMyTimer::Initial(Bool bSingleton)
{
    m_bSingleton = bSingleton;
    return Initial();
}

Bool CMyTimer::Initial()
{
    ASSERT(nullptr == m_MsgMap);
    ASSERT(nullptr == m_spTimer);
    ASSERT(nullptr == m_spThis);
    ASSERT(m_mpOnceTask.empty());
    ASSERT(m_mpFixRateTask.empty());
    m_MsgMap = shared_ptr<CMessageMap>(new CMessageMap(m_spThread.get()));

    return True;
}

Bool CMyTimer::StartTimer(UInt32 nInterval)
{
    ASSERT(m_spThis == nullptr);
    ASSERT(m_MsgMap != nullptr);
    ASSERT(m_spThread != nullptr);

    Bool bRet = False;
    do
    {
        m_spTimer = shared_ptr<CTimer>(new CTimer(m_spThread.get(), this));
        if (!m_spTimer->SetTimer(nInterval))
        {
            m_MsgMap->PostMessage(&CMyTimer::KillTimer, shared_from_this());
            break;
        }
        m_spThis = shared_from_this();
        bRet = True;
    }while (0);

    return bRet;
}

void CMyTimer::ScheduleAtOnce(TimerCallback pCallback)
{
    ASSERT(m_MsgMap != nullptr);
    m_MsgMap->PostMessage(&CMyTimer::DoNowScheduleMsg, pCallback, shared_from_this());
}

void CMyTimer::ScheduleOnceDelay(UInt32 nDelayIntervalCount, TimerCallback pTimerCallback)
{
    ASSERT(m_MsgMap != nullptr);
    ASSERT(m_spTimer != nullptr);
    ASSERT(nDelayIntervalCount > 0);
    shared_ptr<STimerTask> spTask(new STimerTask{nDelayIntervalCount, pTimerCallback});
    m_MsgMap->PostMessage(&CMyTimer::DoSetScheduleTaskMsg, ETSKTYPE_ONCE, spTask, shared_from_this());
}

void CMyTimer::ScheduleAtFixedRate(UInt32 nDelayIntervalCount, TimerCallback pTimerCallback)
{
    ASSERT(m_MsgMap != nullptr);
    ASSERT(m_spTimer != nullptr);
    ASSERT(nDelayIntervalCount > 0);

    shared_ptr<STimerTask> spTask(new STimerTask{nDelayIntervalCount, pTimerCallback});
    m_MsgMap->PostMessage(&CMyTimer::DoSetScheduleTaskMsg, ETSKTYPE_FIXED_RATE, spTask, shared_from_this());
}

void CMyTimer::CleanSchedule()
{
    if (m_MsgMap != nullptr)
    {
        m_MsgMap->PostMessage(&CMyTimer::DoCleanSchedule, shared_from_this());
    }
}

void CMyTimer::DoCleanSchedule()
{
    Assert(OS::GetThreadID() == m_spThread->GetEngineID());
    m_bSingleton = false;
    m_wpInstance.reset();
    m_mpOnceTask.clear();
    m_mpFixRateTask.clear();
}

void CMyTimer::DoSetScheduleTaskMsg(CMyTimer::ETaskType eTaskType, shared_ptr<CMyTimer::STimerTask> spTask)
{
    switch (eTaskType)
    {
        case ETSKTYPE_ONCE:
        {
            m_mpOnceTask[spTask] = spTask->nDelayIntervalCount;
        }
            break;

        case ETSKTYPE_FIXED_RATE:
        {
            m_mpFixRateTask[spTask] = spTask->nDelayIntervalCount;
        }
            break;

        default:
            ASSERT(False);
            break;
    }
}

void CMyTimer::DoNowScheduleMsg(TimerCallback callback)
{
    ASSERT(callback != nullptr);
    shared_ptr<CTimerCallbackInstance> spInstance = m_wpInstance.lock();
    if (m_bSingleton || spInstance)
    {
        callback();
    }
}

void CMyTimer::OnTimer(CTimer *pTimer)
{
    if (!CheckDestroy())
    {
        OnceTask();
        FixedTask();
    }
}

void CMyTimer::OnceTask()
{
    vector<shared_ptr<STimerTask>> vecDel;
    for (auto &item : m_mpOnceTask)
    {
        item.second--;
        if (0 == item.second)
        {
            shared_ptr<CTimerCallbackInstance> spInstance = m_wpInstance.lock();
            if (m_bSingleton || spInstance)
            {
                item.first->pCallback();
            }
            vecDel.push_back(item.first);
        }
    }
    for (auto &item : vecDel)
    {
        m_mpOnceTask.erase(item);
    }
}

void CMyTimer::FixedTask()
{
    for (auto &item : m_mpFixRateTask)
    {
        item.second--;
        if (0 == item.second)
        {
            item.second = item.first->nDelayIntervalCount;
            shared_ptr<CTimerCallbackInstance> spInstance = m_wpInstance.lock();
            if (m_bSingleton || spInstance)
            {
                item.first->pCallback();
            }
        }
    }
}

Bool CMyTimer::CheckDestroy()
{
    Bool bRet = false;
    if (m_spThis.unique())
    {
        KillTimer();
        DoCleanSchedule();
        m_spThis = nullptr;
        bRet = true;
    }
    return bRet;
}

void CMyTimer::KillTimer()
{
    m_spTimer->KillTimer();
    m_spTimer = nullptr;
}
